﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProgNetComponents.Collections;
using System.IO;
using ProgNetComponents.Interfaces;
using ProgNetComponents.Classes;
using System.Collections;
using EnvDTE;
using System.ComponentModel.Design;

namespace ProgNetComponents.Tools
{
    /// <summary>
    /// Helper class for generating files with const values
    /// </summary>
    public static class ConstHelper
    {
        /// <summary>
        /// TabChar
        /// </summary>
        private static string TabChar = "    ";

        /// <summary>
        /// Gets class friendly name
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static string GetFriendlyName(string name)
        {
            name = name.ReplacePolishChars();
            name = name.FirstLetterUpper();
            
            name = name.XReplace("[^a-zA-Z0-9_]*", "");
            if (!name.XMatch("^[a-zA-Z_]"))
                name = "_" + name;
            return name;
        }

        /// <summary>
        /// Adds the consts.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="collection">The collection.</param>
        /// <param name="tabs">The tabs.</param>
        /// <param name="valuePrefix">The value prefix.</param>
        public static void AddConsts(StringBuilder builder, ICollection collection, int tabs, string valuePrefix = "")
        {
            string tab = GetTabsChars(tabs);

            string value;
            string name;

            foreach (object o in collection)
            {
                if(o is INamedObject)
                {
                    value = (o as INamedObject).Name;
                    if (value != null)
                    {
                        name = ProgNetComponents.Tools.ConstHelper.GetFriendlyName(value);
                        builder.AppendLine(tab + "public const string " + name + " = " + (valuePrefix + value.Replace("\"", "\\\"")).AddDoubleQuotes() + ";");
                    }
                }
            }
        }
        /// <summary>
        /// Adds the consts.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="itemType">Type of the item.</param>
        /// <param name="collection">The collection.</param>
        /// <param name="tabs">The tabs.</param>
        /// <param name="className">Name of the class.</param>
        /// <param name="valuePrefix">The value prefix.</param>
        public static void AddConsts(StringBuilder builder, Type itemType, ICollection collection, int tabs, string className = "", string valuePrefix = "")
        {
            string tab = GetTabsChars(tabs);

            string value;
            string name;
            if (className.IsNotNull())
            {
                className = ProgNetComponents.Tools.ConstHelper.GetFriendlyName(className);
                builder.AppendLine(tab + "public static class " + className);
                builder.AppendLine(tab + "{");
                tab += TabChar;
            }
            foreach (object o in collection)
            {
                value = null;
                if (o is INamedObject)
                {
                    value = (o as INamedObject).Name;
                }
                else
                {
                    value = o.AsString();
                }
                if (value != null)
                {
                    name = ProgNetComponents.Tools.ConstHelper.GetFriendlyName(value);
                    if (itemType == typeof(string))
                    {
                        value = (valuePrefix + value.Replace("\"", "\\\"")).AddDoubleQuotes();
                    }
                    else
                    {
                        object v = value.ChangeType(itemType);
                        value = v.AsString();
                    }
                    builder.AppendLine(tab + "public const " + itemType.FullName + " " + name + " = " + value + ";");
                }
            }
            if (className.IsNotNull())
            {
                tab = GetTabsChars(tabs);
                builder.AppendLine(tab + "}");
            }
        }
        /// <summary>
        /// Adds the class start.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="name">The name.</param>
        /// <param name="tabs">The tabs.</param>
        public static void AddClassStart(StringBuilder builder, string name, int tabs)
        {
            string tab = GetTabsChars(tabs);

            builder.AppendLine(tab + "public static class " + name);
            builder.AppendLine(tab + "{");
        }
        /// <summary>
        /// Ends the class.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="tabs">The tabs.</param>
        public static void EndClass(StringBuilder builder, int tabs)
        {
            string tab = GetTabsChars(tabs);
            builder.AppendLine(tab + "}");
            builder.AppendLine();
        }

        /// <summary>
        /// Gets the tabs chars.
        /// </summary>
        /// <param name="tabs">The tabs.</param>
        /// <returns></returns>
        public static string GetTabsChars(int tabs)
        {
            string tab = "";
            for (int i = 0; i < tabs; i++)
                tab += TabChar;
            return tab;
        }
        /// <summary>
        /// Adds the enums.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="name">The name.</param>
        /// <param name="collection">The collection.</param>
        /// <param name="tabs">The tabs.</param>
        public static void AddEnums(StringBuilder builder, string name, ICollection collection, int tabs)
        {
            string tab = GetTabsChars(tabs);

            string value;
            builder.AppendLine(tab + "public enum " + name);
            builder.AppendLine(tab + "{");

            foreach (object o in collection)
            {
                if (o is INamedObject)
                {
                    value = (o as INamedObject).Name;
                    if (value != null)
                    {
                        name = ProgNetComponents.Tools.ConstHelper.GetFriendlyName(value);
                        builder.AppendLine(tab + TabChar + name +",");
                    }
                }
            }
            
            builder.AppendLine(tab + "}");
        }

        /// <summary>
        /// Generates the constant class.
        /// </summary>
        /// <param name="collection">The collection.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="ClassName">Name of the class.</param>
        /// <param name="BaseClassName">Name of the base class.</param>
        public static void GenerateConstClass(ICollection collection, IServiceProvider provider, string ClassName, string BaseClassName)
        {
            if (provider != null)
            {
                DTE dte = provider.GetService<DTE>();
                if (dte != null)
                {
                    string file = dte.ActiveDocument.FullName;
                    string newFile = Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + ClassName + ".cs";
                    string shortName = Path.GetFileName(newFile);

                    StreamWriter writer = new StreamWriter(newFile);
                    IDesignerHost host =  provider.GetService<IDesignerHost>();
                    if (host != null)
                    {
                        string ns = "";
                        string cn = "";
                        Type t = Type.GetType(host.RootComponentClassName);
                        if (t != null)
                        {
                            ns = t.Namespace;
                            cn = t.Name;
                        }
                        else
                        {
                            int idx = host.RootComponentClassName.LastIndexOf('.');
                            if (idx > 0)
                            {
                                ns = host.RootComponentClassName.Substring(0, idx);
                                cn = host.RootComponentClassName.Substring(idx + 1);
                            }
                        }
                        StringBuilder builder = new StringBuilder();

                        builder.AppendLine("namespace " + ns);
                        builder.AppendLine("{");
                        builder.AppendLine(TabChar + "public class " + cn + ClassName + " : " + BaseClassName);
                        builder.AppendLine(TabChar + "{");

                        AddConsts(builder, collection, 2);

                        builder.AppendLine(TabChar + "}");
                        builder.AppendLine("}");

                        writer.Write(builder.ToString());
                        writer.Flush();
                        writer.Close();
                        dte.ActiveDocument.ProjectItem.ContainingProject.ProjectItems.AddFromFile(newFile);
                    }
                }
            }
        }
    }
}
